﻿#ifndef __INC_QG_OGL_MTL_H__
#define __INC_QG_OGL_MTL_H__

#ifdef _MSC_VER
#pragma once
#endif

#include "qg/qgimage.h"
#include "qg_subtex.h"

//////////////////////////////////////////////////////////////////////////
// 텍스쳐
class GLTexture : public qgTexture
{
	KCH_SUBCLASS(GLTexture, qgTexture);
	KCH_FRIEND(GLDevice);

protected:
	kuint				_awidth : 16;
	kuint				_aheight : 16;
	kuint				_adepth : 16;
	kuint				_atmp : 16;

	qgImage*			_img_rsw;
	qgImage*			_img_mip;

	GLint				_gl_fmt;
	GLenum				_gl_type;
	GLenum				_pixel_fmt;
	GLenum				_pixel_type;

	qgSplrData			_splr;

protected:
	GLTexture() throw();
	~GLTexture() throw();

public:
	K_OVR bool EnterMap(qgTextureMap* map, kint flag, kint mips, qgTextureFace face);
	K_OVR bool LeaveMap();

protected:
	bool _InitImage(const char* name, qgImage* img, kint mips, kint flags, GLDevice* dev);
	bool _InitProperty(qgTextureType type, const char* name, kint width, kint height, kint depth, kint mips, kint flags, qgColorFormat fmt, GLDevice* dev);
	bool _CreateInternal(GLuint* ptex, GLDevice* dev);
	bool _CreateTexture(qgImage* img, GLDevice* dev);
	void _UploadTexture(bool isnew, kint mipno, GLDevice* dev);
};


//////////////////////////////////////////////////////////////////////////
// 모션 텍스쳐, 클래스의 이중 상속...이 있다
class GLTextureMotion : public GLTexture, public qgSubTextureMotion
{
	KCH_SUBCLASS(GLTextureMotion, GLTexture);
	KCH_FRIEND(GLDevice);

private:
	GLTextureMotion() throw();
	~GLTextureMotion() throw();

public:
	K_OVR qgITextureMotion* GetMotion();

	// qgITextureMotion
	K_OVR bool MotionUpdate(float adv);

private:
	bool _CreateMotion(const char* filename, kint flags, GLDevice* dev);
};


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
class GLDepthBuffer : public qgDepthBuffer
{
	KCH_SUBCLASS(GLDepthBuffer, qgDepthBuffer);
	KCH_FRIEND(GLDevice);
	KCH_FRIEND(GLTextureFBO);

public:
	enum BindType
	{
		BindDepthAndStencil,
		BindDepthWithStencil,
		BindDepthOnly,
	};

private:
	BindType			_bind;
	GLTextureFBO*		_lastfbo;

protected:
	GLDepthBuffer() throw();
	~GLDepthBuffer() throw();

public:
	GLTextureFBO* GetLastAttach() { return _lastfbo; }
	bool Attach(GLTextureFBO* fbo);

private:
	bool _Create(kuint width, kuint height, GLDevice* dev);
};


//////////////////////////////////////////////////////////////////////////
// 오프 텍스쳐
class GLTextureFBO : public GLTexture, public qgITextureOff
{
	KCH_SUBCLASS(GLTextureFBO, GLTexture);
	KCH_FRIEND(GLDevice);
	KCH_FRIEND(GLTexture);
	KCH_FRIEND(GLDepthBuffer);

private:
	GLuint				_fbo;
	GLDepthBuffer*		_db;

	bool				_lock_off;

private:
	GLTextureFBO() throw();
	~GLTextureFBO() throw();

public:
	K_OVR qgITextureOff* GetOff();

	// qgITextureOff
	K_OVR bool EnterOff(kint clearance, const tpl::ColorValue& color);
	K_OVR bool LeaveOff();

private:
	bool _CreateOff(GLDevice* dev);

	static const char* _CheckFBO();
};


//////////////////////////////////////////////////////////////////////////
// 래스터라이저
class GLRasz : public qgRasz
{
	KCH_SUBCLASS(GLRasz, qgRasz);
	KCH_FRIEND(GLDevice);

private:
	GLRasz() throw() {}
	~GLRasz() throw() {}
};


//////////////////////////////////////////////////////////////////////////
// 블렌드
class GLBlnd : public qgBlnd
{
	KCH_SUBCLASS(GLBlnd, qgBlnd);
	KCH_FRIEND(GLDevice);

private:
	GLBlnd() throw() {}
	~GLBlnd() throw() {}
};


//////////////////////////////////////////////////////////////////////////
// 샘플러
class GLSplr : public qgSplr
{
	KCH_SUBCLASS(GLSplr, qgSplr);
	KCH_FRIEND(GLDevice);

private:
	GLSplr() throw() {}
	~GLSplr() throw() {}
};


//////////////////////////////////////////////////////////////////////////
// 세이더
class GLProgram : public qgShader
{
	KCH_SUBCLASS(GLProgram, qgShader);
	KCH_FRIEND(GLDevice);
	KT_DECL_TYPE_INIT(GLProgram);

public:
	struct RefHandle : public tpl::MemBase
	{
		kint			ref;
		GLuint			handle;

		RefHandle(GLuint handle) : ref(1), handle(handle) {}
	};

	struct AttribVar
	{
		ksize_t				hash;
		tpl::BlueStr<32>	name;

		qgShaderData		data : 16;
		kuint				size : 16;
		GLint				offset;

		qgDloUsage			usage : 16;
		kint				index : 16;	// usage 인덱스

		AttribVar*			next;
	};

	typedef tpl::Array<qgShaderVar> VarArray;
	typedef tpl::Array<AttribVar> AttribArray;

private:
	RefHandle*			_rfp;
	RefHandle*			_rvp;

	VarArray			_vars;
	AttribArray			_atrs;

	kint				_acnt[QGDLOU_MAX_VALUE];
	AttribVar*			_alnk[QGDLOU_MAX_VALUE];

	bool				_linked;

private:
	GLProgram() throw();
	~GLProgram() throw();

public:
	K_OVR bool BindFile(qgShaderType type, const char* filename, kint flags) throw();
	K_OVR bool BindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw();
	K_OVR bool BindShader(qgShaderType type, const char* name) throw();
	K_OVR bool Link();

	K_OVR qgShaderVar* GetVars(kint stage, kint* count);

	//
	AttribVar* FindAttribUsage(qgDloUsage usage, kint index) throw();
	GLint FindFixedAttribUsage(qgDloUsage usage, kint index) throw();

private:
	void _PrintError(GLuint handle, kint errcode, const char* target, bool shdinfo, bool prginfo) throw();
	RefHandle* _CompileProgram(qgShaderType type, const char* src, GLint len) throw();
	bool _InternalBindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw();

	static void _RemoveHandle(RefHandle* rh, GLuint program) throw();
	static qgShaderData _GlTypeToShaderData(GLenum type) throw();
	static qgDloUsage _AttribNameToLayoutUsage(const char* name) throw();

	static kpointer _MatchAutoFunc(qgShaderVar& v) throw();
	static void _InitAutoFunc() throw();

	void _ProcessMap() throw();
};

#endif	// __INC_QG_OGL_MTL_H__
